#include "PLMosaic.h"
#include "PLConversionMethods.h"
#include "PLBlob.h"
#include "PLEdge.h"
#include "PLEnums.h"
#include "PLStringMethods.h"
#include "PLPlanarPolygon3dsOptimization.h"
#include "PLVertex.h"
#include "PLVertexFactory.h"
#include "PLImageFactory.h"
#include "PLTriangle.h"
#include "PLTimer.h"

#include <iostream>
#include <iomanip>
#include <string>
#include <vector>

using namespace PL;

using std::cout;
using std::endl;
using std::left;
using std::right;
using std::setw;
using std::string;
using std::vector;

void printUsage( void )
{
	cout << endl << "Usage:\n\tPoissonLeveler-vc100-<PlatformName> <objModelFilePath>" << endl << endl;
	cout << "Optional:\n";
	cout << "\t" << left << setw( 20 ) << "--Option--" << setw( 30 ) << "--Description--" << right << setw( 11 ) << "--Default--" << endl;
	cout << "\t" << left << setw( 20 ) << "-minres=<value>" << setw( 30 ) << "Minimum Residual" << right << setw( 6 ) << "0" << ".0" << endl;
	cout << "\t" << left << setw( 20 ) << "-maxit=<value>" << setw( 30 ) << "Maximum number of iterations" << right << setw( 6 ) << "1000" << endl;
	cout << "\t" << left << setw( 20 ) << "-mlss=<value>" << setw( 30 ) << "Max.num. of line search steps" << right << setw( 6 ) << "1000" << endl;
	cout << "\t" << left << setw( 20 ) << "-mingrad=<value>" << setw( 30 ) << "Min. Gradient abs. component" << right << setw( 6 ) << "1" << ".0e-6" << endl;
	cout << "\t" << left << setw( 20 ) << "-stw=<value>" << setw( 30 ) << "Second term weight" << right << setw( 6 ) << "100" << ".0" << endl;
	cout << "\t" << left << setw( 20 ) << "-ttw=<value>" << setw( 30 ) << "Third term weight" << right << setw( 6 ) << "0" << ".001" << endl;
	cout << "\t" << left << setw( 20 ) << "-vnl=<value>" << setw( 30 ) << "Vertex normal length [px]" << right << setw( 6 ) << "20" << endl;
	cout << "\t" << left << setw( 20 ) << "-om=<value>" << setw( 30 ) << "Gen. Outline Masks" << right << setw( 6 ) << "0" << endl;
	cout << "\t" << left << setw( 20 ) << "-ms=<value>" << setw( 30 ) << "Gen. Mesh Masks" << right << setw( 6 ) << "0" << endl << endl;
}

int main( int argc, const char* argv[] )
{
	if (argc < 2)
	{
		cout << "Please provide a model file path!" << endl;
		printUsage();
		return 0;
	}
	
	PLMosaic *mosaic;
	double minres = 0.0;
	PLuint maxit = 1000U;
	PLuint mlss = 1000U;
	double mingrad = 1.0e-6;
	double stw = 100.0;
	double ttw = 0.001;
	PLuint vertexNormalLength = 20U;
	bool om = false;
	bool ms = false;

	vector<string> tokens;

	for (int i = 2; i < argc; i++)
	{
		tokens.clear();

		if (StringStartsWith( string( argv[i] ), "-minres=" ))
		{
			Tokenize( string( argv[i] ), &tokens, "=" );
			minres = StringToDouble( tokens[1] );
		}
		else if (StringStartsWith( string( argv[i] ), "-maxit=" ))
		{
			Tokenize( string( argv[i] ), &tokens, "=" );
			maxit = StringToPLuint( tokens[1] );
		}
		else if (StringStartsWith( string( argv[i] ), "-mlss=" ))
		{
			Tokenize( string( argv[i] ), &tokens, "=" );
			mlss = StringToPLuint( tokens[1] );
		}
		else if (StringStartsWith( string( argv[i] ), "-mingrad=" ))
		{
			Tokenize( string( argv[i] ), &tokens, "=" );
			mingrad = StringToDouble( tokens[1] );
		}
		else if (StringStartsWith( string( argv[i] ), "-stw=" ))
		{
			Tokenize( string( argv[i] ), &tokens, "=" );
			stw = StringToDouble( tokens[1] );
		}
		else if (StringStartsWith( string( argv[i] ), "-ttw=" ))
		{
			Tokenize( string( argv[i] ), &tokens, "=" );
			ttw = StringToDouble( tokens[1] );
		}
		else if (StringStartsWith( string( argv[i] ), "-vnl=" ))
		{
			Tokenize( string( argv[i] ), &tokens, "=" );
			vertexNormalLength = StringToPLuint( tokens[1] );
		}
		else if (StringStartsWith( string( argv[i] ), "-om=" ))
		{
			Tokenize( string( argv[i] ), &tokens, "=" );
			om = StringToPLuint( tokens[1] ) != 0 ? true : false;
		}
		else if (StringStartsWith( string( argv[i] ), "-ms=" ))
		{
			Tokenize( string( argv[i] ), &tokens, "=" );
			ms = StringToPLuint( tokens[1] ) != 0 ? true : false;
		}
	}

	//string obj = "E:\\Michael\\Uni\\12._Semester_SS11\\Diplomarbeit\\Daten\\Centcelles\\cent_kuppel_meter\\MRF\\centcelles_MRF_w25.obj";
	//string obj = "E:\\Michael\\Uni\\12._Semester_SS11\\Diplomarbeit\\Daten\\Centcelles\\cent_kuppel_meter\\MRF\\centcelles_MRF_w25_r0.95.obj";
	//string obj = "E:\\Michael\\Uni\\12._Semester_SS11\\Diplomarbeit\\Daten\\Centcelles\\cent_kuppel_meter\\MRF\\small_part.obj";

	// E:\\Michael\\Uni\\12._Semester_SS11\\Diplomarbeit\\Daten\\Centcelles\\cent_kuppel_meter\\Results\\cent_kuppel_meter__numberOfLabels_50__numberOfFaces_150000__useOctree_no__cellSize_0.1__numIterations_2__smoothCost_25.obj

	string obj = "E:\\Michael\\Uni\\12._Semester_SS11\\Diplomarbeit\\Daten\\Centcelles\\cent_kuppel_meter\\Results\\cent_kuppel_meter__numberOfLabels_70__numberOfFaces_300000__useOctree_no__cellSize_0.1__numIterations_2__smoothCost_25.obj";

	minres = 1.0e-6;
	maxit = 1000U;
	mlss = 1000U;
	mingrad = 1.0e-6;

	ttw = 0.00;
	vertexNormalLength = 20u;

	try
	{
		/*
		PLMosaic( const std::string &modelFilePath,
		const PLModelFormat &modelFileFormat,
		const std::string &leveledModelFilePath,
		const PLModelFormat &leveledModelFileFormat,
		const PLFilteringMode &filteringMode = PL_FILTERMODE_NEAREST_NEIGHBOR,
		PLuint outlineVertexNormalLength = 20U,
		const PLImageFormat &imageFormat = PL_IMAGEFORMAT_JPEG ) throw( PLIOException, PLFileNotFoundException );
		*/
		//mosaic = new PLMosaic( obj, PL_MODELFORMAT_OBJ, "E:\\Michael\\Uni\\12._Semester_SS11\\Diplomarbeit\\Daten\\Centcelles\\cent_kuppel_meter\\MRF\\small_part_ttw0.01\\small_part.obj", PL_MODELFORMAT_OBJ, PL_FILTERMODE_NEAREST_NEIGHBOR, vertexNormalLength );
		//mosaic = new PLMosaic( obj, PL_MODELFORMAT_OBJ, "E:\\Michael\\Uni\\12._Semester_SS11\\Diplomarbeit\\Daten\\Mal03\\leveled_0.01_ONL_20\\Mal03-texture_leveled.obj", PL_MODELFORMAT_OBJ, PL_FILTERMODE_NEAREST_NEIGHBOR, vertexNormalLength );
		
		mosaic = new PLMosaic( obj, PL_MODELFORMAT_OBJ, "E:\\Michael\\Uni\\12._Semester_SS11\\Diplomarbeit\\Daten\\Centcelles\\cent_kuppel_meter\\Results\\leveled\\cent_kuppel_meter__numberOfLabels_70__numberOfFaces_300000__useOctree_no__cellSize_0.1__numIterations_2__smoothCost_25leveled.obj", PL_MODELFORMAT_OBJ, PL_FILTERMODE_NEAREST_NEIGHBOR, vertexNormalLength );
	}
	catch( PLIOException &e )
	{
		cout << e.what() << endl;
		return 0;
	}
	catch( PLFileNotFoundException &e )
	{
		cout << e.what() << endl;
		return 0;
	}

	PLPlanarPolygon3dsOptimizationOptions options( minres, maxit, mlss, mingrad, stw, ttw );
	
	PLTimer timer;
	cout << "Calculating leveling function" << endl;
	timer.Toc();
	mosaic->CalculateLevellingFunction( &options );
	cout << "Ready! This took " << timer.Toc() << " seconds" << endl;

	mosaic->WriteLeveledModelFile();

	PLImageFactory image_factory( mosaic );

 	cout << "Generating leveled textures..." << endl;
	//image_factory.GenerateLevelledTextures( "E:\\Michael\\Uni\\12._Semester_SS11\\Diplomarbeit\\Daten\\Centcelles\\cent_kuppel_meter\\Results\\leveled" );
 	
	//image_factory.GenerateLevelledTextures( "E:\\Michael\\Uni\\12._Semester_SS11\\Diplomarbeit\\Daten\\Centcelles\\cent_kuppel_meter\\MRF\\small_part_ttw0.01" );
// 	cout << "Generating levelling function textures..." << endl;
// 	image_factory.GenerateLevellingFunctionTextures();
// 	cout << "Generating levelling function value textures..." << endl;
// 	image_factory.GenerateLevellingFunctionValueTextures( PL_CHANNEL_RED );
// 	image_factory.GenerateLevellingFunctionValueTextures( PL_CHANNEL_GREEN );
// 	image_factory.GenerateLevellingFunctionValueTextures( PL_CHANNEL_BLUE );

	if (om)
	{
		cout << "Generating outline mask..." << endl;
		//image_factory.GenerateAreaMasks( "E:\\Michael\\Uni\\12._Semester_SS11\\Diplomarbeit\\Daten\\Centcelles\\cent_kuppel_meter\\Results\\leveled\\area_masks" );
	}

	if (ms)
	{
		cout << "Generating Mesh-Structure mask..." << endl;
		//image_factory.GenerateMeshStructureMasks( "E:\\Michael\\Uni\\12._Semester_SS11\\Diplomarbeit\\Daten\\Centcelles\\cent_kuppel_meter\\Results\\leveled\\mesh_masks", true );
	}

// 	cout << "Num Edgs: " << PLEdge::GetNumEdges() << endl;
// 	cout << "Num Vertexes: " << PLVertex::GetNumVertexes() << endl;
// 	cout << "Num Blobs: " << PLBlob::GetNumBlobs() << endl;
// 	cout << "Num Triangles: " << PLTriangle::GetNumTriangles() << endl << endl;

	delete mosaic;
	mosaic = 0;

// 	cout << "Num Edgs: " << PLEdge::GetNumEdges() << endl;
// 	cout << "Num Vertexes: " << PLVertex::GetNumVertexes() << endl;
// 	cout << "Num Blobs: " << PLBlob::GetNumBlobs() << endl;
// 	cout << "Num Triangles: " << PLTriangle::GetNumTriangles() << endl;

	cout << "Ready!" << endl;

	return 0;
}